package cn.skcks.springboot09redis.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
@SuppressWarnings("unused")
public final class RedisUtils {
	private TimeUnit defaultTimeUnit = TimeUnit.MILLISECONDS;

	public TimeUnit getDefaultTimeUnit() {
		return defaultTimeUnit;
	}

	public void setDefaultTimeUnit(TimeUnit defaultTimeUnit) {
		this.defaultTimeUnit = defaultTimeUnit;
	}

	private RedisTemplate<String, Object> redisTemplate;

	@Autowired
	public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	public RedisTemplate<String, Object> getRedisTemplate() {
		return redisTemplate;
	}

	/**
	 * 设置缓存失效时间
	 *
	 * @param key      键名
	 * @param time     时间
	 * @param timeUnit 时间单位
	 * @return 设置状态
	 */
	public boolean setExpire(String key, long time, TimeUnit timeUnit) {
		try {
			if (time > 0) {
				redisTemplate.expire(key, time, timeUnit);
			}
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 设置缓存失效时间
	 *
	 * @param key          键名
	 * @param milliseconds 时间（毫秒）
	 * @return 设置状态
	 */
	public boolean setExpire(String key, long milliseconds) {
		return setExpire(key, milliseconds, defaultTimeUnit);
	}

	/**
	 * 获取缓存失效时间
	 *
	 * @param key      键名
	 * @param timeUnit 时间单位
	 * @return 时间
	 */
	public long getExpire(String key, TimeUnit timeUnit) {
		Long time = redisTemplate.getExpire(key, timeUnit);
		if (time == null) {
			time = -1L;
		}
		return time;
	}

	/**
	 * 获取缓存失效时间
	 *
	 * @param key 键名
	 * @return 时间 (毫秒)
	 */
	public long getExpire(String key) {
		return getExpire(key, defaultTimeUnit);
	}

	/**
	 * key 是否存在
	 *
	 * @param key 键名
	 * @return 是否存在
	 */
	public boolean hasKey(String key) {
		Boolean has = redisTemplate.hasKey(key);
		if (has == null) {
			has = false;
		}
		return has;
	}

	/**
	 * 删除缓存
	 *
	 * @param keys 键名
	 */
	public void del(String... keys) {
		if (keys == null) {
			return;
		}
		redisTemplate.delete(Arrays.asList(keys));
	}

	/**
	 * 获取缓存
	 *
	 * @param key 键值
	 * @param cls 泛型类
	 * @return 值
	 */
	@SuppressWarnings(value = {"unchecked", "unused"})
	public <T> T get(String key, Class<T> cls) {
		if (key == null) {
			return null;
		}
		Object obj = redisTemplate.opsForValue().get(key);
		return (T) (obj);
	}

	/**
	 * 获取缓存
	 *
	 * @param key 键值
	 * @return 值
	 */
	public Object get(String key) {
		return get(key, Object.class);
	}

	/**
	 * 设置缓存
	 *
	 * @param key   键值
	 * @param value 值
	 * @return 设置状态
	 */
	public boolean set(String key, Object value) {
		try {
			redisTemplate.opsForValue().set(key, value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 设置缓存
	 *
	 * @param key      键值
	 * @param value    值
	 * @param time     时间
	 * @param timeUnit 时间单位
	 * @return 设置状态
	 */
	public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
		try {
			if (time > 0) {
				redisTemplate.opsForValue().set(key, value, time, timeUnit);
			} else {
				return set(key, value);
			}
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 设置缓存
	 *
	 * @param key   键值
	 * @param value 值
	 * @param time  时间
	 * @return 设置状态
	 */
	public boolean set(String key, Object value, long time) {
		return set(key, value, time, defaultTimeUnit);
	}

	/**
	 * 递增
	 *
	 * @param key   键值
	 * @param delta 增量
	 * @return 新值
	 */
	public long increment(String key, long delta) {
		if (delta < 0) {
			throw new RuntimeException("Increment must be equal or greater than 0");
		}
		Long res = redisTemplate.opsForValue().increment(key, delta);
		return res == null ? 0L : res;
	}

	/**
	 * 递减
	 *
	 * @param key   键值
	 * @param delta 增量
	 * @return 新值
	 */
	public long decrement(String key, long delta) {
		if (delta > 0) {
			throw new RuntimeException("Decrement must be equal or less than 0");
		}
		Long res = redisTemplate.opsForValue().decrement(key, delta);
		return res == null ? 0L : res;
	}

	/**
	 * HashGet
	 *
	 * @param key  键名
	 * @param item 项名
	 * @return 值
	 */
	public Object hashGet(String key, String item) {
		return redisTemplate.opsForHash().get(key, item);
	}

	/**
	 * HashSet
	 *
	 * @param key   键名
	 * @param item  项名
	 * @param value 值
	 * @return 设置状态
	 */
	public boolean hashSet(String key, String item, Object value) {
		try {
			redisTemplate.opsForHash().put(key, item, value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * HashSet
	 *
	 * @param key      键名
	 * @param item     项名
	 * @param value    值
	 * @param time     时间
	 * @param timeUnit 时间单位
	 * @return 设置状态
	 */
	public boolean hashSet(String key, String item, Object value, long time, TimeUnit timeUnit) {
		if (hashSet(key, item, value)) {
			setExpire(key, time, timeUnit);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * HashSet
	 *
	 * @param key   键名
	 * @param item  项名
	 * @param value 值
	 * @param time  时间
	 * @return 设置状态
	 */
	public boolean hashSet(String key, String item, Object value, long time) {
		return hashSet(key, item, value, time, defaultTimeUnit);
	}

	/**
	 * 删除 hash 表中的值
	 *
	 * @param key  键名
	 * @param item 项名
	 */
	public void hashDelete(String key, Object... item) {
		redisTemplate.opsForHash().delete(key, (Object) item);
	}

	/**
	 * hash 中是否存在该项的值
	 *
	 * @param key  键值
	 * @param item 项名
	 * @return 是否存在
	 */
	public boolean hashHasKey(String key, String item) {
		return redisTemplate.opsForHash().hasKey(key, item);
	}


	/**
	 * HashGet Key 对应所有键值
	 *
	 * @param key 键名
	 * @return 所有的值对象
	 */
	public Map<Object, Object> hashMapGet(String key) {
		return redisTemplate.opsForHash().entries(key);
	}

	/**
	 * HashSet
	 *
	 * @param key 键名
	 * @param map 整个值对象
	 * @return 设置状态
	 */
	public boolean hashMapSet(String key, Map<String, Object> map) {
		try {
			redisTemplate.opsForHash().putAll(key, map);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * HashSet
	 *
	 * @param key      键名
	 * @param map      整个值对象
	 * @param time     时间
	 * @param timeUnit 时间单位
	 * @return 设置状态
	 */
	public boolean hashMapSet(String key, Map<String, Object> map, long time, TimeUnit timeUnit) {
		if (hashMapSet(key, map)) {
			setExpire(key, time, timeUnit);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * HashSet
	 *
	 * @param key  键名
	 * @param map  整个值对象
	 * @param time 时间
	 * @return 设置状态
	 */
	public boolean hashMapSet(String key, Map<String, Object> map, long time) {
		return hashMapSet(key, map, time, defaultTimeUnit);
	}

	/**
	 * Hash 递增
	 *
	 * @param key   键名
	 * @param item  项名
	 * @param delta 增量
	 * @return 新值
	 */
	public long hashIncrement(String key, String item, long delta) {
		if (delta < 0) {
			throw new RuntimeException("Increment must be equal or greater than 0");
		}
		return redisTemplate.opsForHash().increment(key, item, delta);
	}

	/**
	 * Hash 递减
	 *
	 * @param key   键名
	 * @param item  项名
	 * @param delta 增量
	 * @return 新值
	 */
	public long hashDecrement(String key, String item, long delta) {
		if (delta > 0) {
			throw new RuntimeException("Decrement must be equal or less than 0");
		}
		return redisTemplate.opsForHash().increment(key, item, -delta);
	}

	/**
	 * 根据 key 获取 set 中 所有值
	 *
	 * @param key 键名
	 * @return 值
	 */
	public Set<Object> setGet(String key) {
		try {
			return redisTemplate.opsForSet().members(key);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 查询 set 对应的 key 中是否存在 value
	 *
	 * @param key   键名
	 * @param value 值
	 * @return 是否存在
	 */
	public boolean setHasKey(String key, Object value) {
		try {
			Boolean has = redisTemplate.opsForSet().isMember(key, value);
			if (has == null) {
				return false;
			}
			return has;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 将数据放入 set
	 *
	 * @param key   键名
	 * @param value 值
	 * @return 成功个数
	 */
	public long setSet(String key, Object... value) {
		Long num = redisTemplate.opsForSet().add(key, value);
		if (num == null) {
			return 0;
		}
		return num;
	}

	/**
	 * 将数据放入 set
	 * @param key 键名
	 * @param time 时间
	 * @param timeUnit 时间范围
	 * @param value 值
	 * @return 成功个数
	 */
	public long setSetWithTime(String key, long time, TimeUnit timeUnit, Object... value) {
		long num = setSet(key, value);

		if (num == 0) {
			return num;
		}

		setExpire(key, time, timeUnit);
		return num;
	}

	/**
	 * 将数据放入 set
	 * @param key 键名
	 * @param time 时间
	 * @param value 值
	 * @return 成功个数
	 */
	public long setSetWithTime(String key, long time, Object... value) {
		return setSet(key, time, defaultTimeUnit, value);
	}

	/**
	 * 获取 set 长度
	 * @param key 键名
	 * @return 长度
	 */
	public long setSize(String key){
		Long length = redisTemplate.opsForSet().size(key);
		if(length == null){
			return 0;
		}
		return length;
	}

	/**
	 * 从 set 中 移除 key 指定值
	 * @param key 键值
	 * @param value 值
	 * @return 移除个数
	 */
	public long setDelete(String key,Object... value){
		Long num = redisTemplate.opsForSet().remove(key,value);
		if(num == null){
			return 0;
		}
		return num;
	}

	/**
	 * 获取 list 内容
	 * @param key 键名
	 * @param start 起始地址
	 * @param end 结束地址
	 * @return list
	 */
	public List<Object> listGet(String key, long start, long end) {
		try {
			return redisTemplate.opsForList().range(key, start, end);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 通过索引取 list 中的值
	 * @param key 键名
	 * @param index 索引下标
	 * @return 值
	 */
	public Object listGetIndex(String key,long index){
		try{
			return redisTemplate.opsForList().index(key,index);
		}catch(Exception e){
			return null;
		}
	}

	/**
	 * 通过值 获取 list 索引下标
	 * @param key 键名
	 * @param value 值
	 * @return 下标
	 */
	@SuppressWarnings("all")
	public long listGetIndexOf(String key,Object value){
		Long index = redisTemplate.opsForList().indexOf(key,value);
		if(index == null){
			return 0;
		}
		return index;
	}

	/**
	 * 获取 list 所有内容
	 * @param key 键名
	 * @return list
	 */
	public List<Object> listGet(String key){
		return listGet(key,0,-1);
	}

	/**
	 * 将值放入 list 缓存
	 * @param key 键名
	 * @param value 值
	 * @return 是否成功
	 */
	public boolean listSet(String key, Object value) {
		try {
			redisTemplate.opsForList().rightPushIfPresent(key, value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 将值放入 list 缓存
	 * @param key 键名
	 * @param value 值
	 * @param time 时间
	 * @param timeUnit 时间单位
	 * @return 是否成功
	 */
	public boolean listSet(String key, Object value, long time, TimeUnit timeUnit) {
		if (listSet(key, value)) {
			setExpire(key, time, timeUnit);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 将值放入 list 缓存
	 * @param key 键名
	 * @param value 值
	 * @param time 时间
	 * @return 是否成功
	 */
	public boolean listSet(String key,Object value,long time){
		return listSet(key, value, time,defaultTimeUnit);
	}

	/**
	 * 将 list 放入缓存
	 * @param key 键名
	 * @param value 值列表
	 * @return 是否成功
	 */
	public boolean listSet(String key,List<Object> value){
		try{
			redisTemplate.opsForList().leftPushAll(key,value);
			return true;
		}catch(Exception e){
			return false;
		}
	}

	/**
	 * 将 list 放入缓存
	 * @param key 键名
	 * @param value 值列表
	 * @param time 时间
	 * @param timeUnit 时间单位
	 * @return 是否成功
	 */
	public boolean listSet(String key,List<Object> value,long time,TimeUnit timeUnit){
		if(listSet(key,value)){
			setExpire(key, time, timeUnit);
			return true;
		}else {
			return false;
		}
	}

	/**
	 * 将 list 放入缓存
	 * @param key 键名
	 * @param value 值列表
	 * @param time 时间
	 * @return 是否成功
	 */
	public boolean listSet(String key,List<Object> value,long time){
		return listSet(key, value, time,defaultTimeUnit);
	}

	/**
	 * 设置 list 下标为 index 的 值
	 * @param key 键名
	 * @param index 下标
	 * @param value 值
	 * @return 是否成功
	 */
	public boolean listSetIndex(String key,long index,Object value){
		try {
			redisTemplate.opsForList().set(key, index, value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 从 list 中 删除 N 个 指定 值
	 * @param key 键名
	 * @param num 为 0 时, 删除所有为 value 的 值 <br/>
	 *            > 0 时, 从 前往后 删除 num 个值 为 value 的值 <br/>
	 *            < 0 时, 从 后往前 删除 -num 个值 为 value 的值
	 * @param value 值
	 * @return 删除个数
	 */
	public long listDelete(String key,long num,Object value){
		try{
			Long count = redisTemplate.opsForList().remove(key, num, value);
			if(count == null){
				return 0;
			}
			return count;
		}catch(Exception e){
			return 0;
		}
	}

	/**
	 * 获取 list 长度
	 * @param key 键名
	 * @return 长度
	 */
	public long listSize(String key){
		Long length = redisTemplate.opsForList().size(key);
		if(length ==null){
			return 0;
		}
		return length;
	}
}